package helexis.game.tyrian.screen.scene2d;

import helexis.game.tyrian.Assets;
import helexis.game.tyrian.Tyrian;
import helexis.game.tyrian.domain.Ship;
import helexis.game.tyrian.domain.ShipModel;
import helexis.game.tyrian.screen.AbstractScreen;

import java.util.HashMap;
import java.util.Map;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Input.Peripheral;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;

public class Ship2D extends Image {
	
	/**
	 * 비행선 위치
	 */
	private final Vector2 position;
	/**
	 * 비행선 속도 (픽셀/초)
	 */
	private final Vector2 velocity;
	/**
	 * 비행선 가속도 (픽셀/초^2)
	 */
	private final Vector2 acceleration;
	
	
	/**
	 * 양 옆으로 움직이는 비행선을 표현하는 애니메이션
	 */
	private final Animation tiltAnimation;
	
	
	
	private final Map<TextureRegion, Drawable> tiltAnimationDrawables;
	
	
	/**
	 * 2D 비행선의 초기화 작업을 수행합니다.
	 * 
	 * @param ship Ship
	 * @param regions 비행선 움직임을 포함하는 AtlasRegion 목록
	 */
	private Ship2D(Ship ship, Array<AtlasRegion> regions) {
		
		/*
		 * 이미지 생성자를 호출하여 초기화 로직을 수행하고,
		 * 터치 불가능하도록 설정함
		 */
		super(regions.get(0));
		super.setTouchable(Touchable.disabled);
		
		/*
		 * 비행선의 물리적인 속성에 대한 초기화
		 */
		this.position = new Vector2();
		this.velocity = new Vector2();
		this.acceleration = new Vector2();
		
		
		/*
		 * 0.15 초 동안 지정된 이미지를 사용하여 애니메이션 표현
		 */
		this.tiltAnimation = new Animation(0.15f, regions);
		
		
		/*
		 * 그릴 수 있는 상태로 캐시에 담아 놓음
		 */
		this.tiltAnimationDrawables = new HashMap<TextureRegion, Drawable>();
		for (AtlasRegion region : regions) {
			this.tiltAnimationDrawables.put(region, new TextureRegionDrawable(region));
		}//end for
		
		
	}//end of constructor
	
	
	/**
	 * Ship2D 를 생성하는 팩토리 메소드
	 * 
	 * @param ship Ship
	 * @param atlas TextureAtlas
	 * @return 생성한 Ship2D 객체
	 */
	public static Ship2D create(Ship ship, TextureAtlas atlas) {
		
		ShipModel shipModel = ship.getShipModel();
		
		Assets.initShipModelImages(atlas);
		
		Array<AtlasRegion> regions = null;
		
		if (shipModel == ShipModel.GENCORE_PHOENIX) {
			regions = Assets.shipModel_GENCORE_PHOENIX;
		} else if (shipModel == ShipModel.GENCORE_II) {
			regions = Assets.shipModel_GENCORE_II;
		} else if (shipModel == ShipModel.MICROSOL_STALKER) {
			regions = Assets.shipModel_MICROSOL_STALKER;
		} else if (shipModel == ShipModel.SUPER_CARROT) {
			regions = Assets.shipModel_SUPER_CARROT;
		} else {
			regions = Assets.shipModel_USP_TALON;
		}//end if else
		
		return new Ship2D(ship, regions);
		
	}//end of create()
	
	
	
	
	/*
	 * Ship 이 움직이기 위한 여러 상수 정의
	 */
	
	/**
	 * ship 이 초당 움직일 수 있는 최대 속도 : 250 픽셀/초
	 */
	private static final float MAX_MOVEMENT_SPEED = 250f;
	
	/**
	 * ship 이 초당 움직일 수 있는 최대 가로 속도 : 화면 끝에서 끝까지 1.6 초 내에 움직일 수 있을 정도의 픽셀/초
	 */
	private static final float MAX_HORIZONTAL_SPEED = (AbstractScreen.GAME_VIEWPORT_WIDTH / 1.6f);
	/**
	 * ship 이 초당 움직일 수 있는 최대 세로 속도 : 화면 끝에서 끝까지 0.96 초 내에 움직일 수 있을 정도의 픽셀/초
	 */
	private static final float MAX_VERTICAL_SPEED = (AbstractScreen.GAME_VIEWPORT_HEIGHT / 0.96f);
	
	
	@Override
	public void act(float delta) {
		super.act(delta);
		this.moveShip(delta);
	}//end of act()
	
	
//	/**
//	 * Ship 을 움직입니다.
//	 * 
//	 * Ship 은 시간 변화량 (delta, δ) 에 따른 움직임 (픽셀/초) 로 계산합니다.
//	 * 
//	 * @param delta 시간 변화량 (초)
//	 */
//	public void moveShip(float delta) {
//		
//		float x = getX();
//		float y = getY();
//		
//		/*
//		 * 
//		 * Tutorial 10.
//		 * 
//		 * 가속도계가 사용 가능한 지 확인하여,
//		 * 사용 가능하다면 이를 입력 장치로 사용하고
//		 * 그렇지 않은 경우에는 키보드 입력을 사용하도록 한다.
//		 * 
//		 */
//		if (Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer)) {
//			
//			if (Tyrian.DEV_MODE) {
//				Gdx.app.log(Tyrian.LOG, "Accelerometer X/Y/Z : " + 
//						Gdx.input.getAccelerometerX() + " / " + 
//						Gdx.input.getAccelerometerY() + " / " + 
//						Gdx.input.getAccelerometerZ());
//			}//end if
//			
//			/*
//			 * 가속도계를 사용하여 Ship 을 움직이는 경우에는
//			 * 가속도계가 던져주는 값을 보정하여 움직임을 지정해야 함
//			 * 
//			 * 참고로, 현재 화면은 landscape 모드로 동작하므로,
//			 * 가속도계가 던져주는 값은 Ship 의 X/Y 좌표와 반대임
//			 */
//			
//			/*
//			 * X : 4(뒤로), 2(그대로), 0(앞으로)
//			 * 이 값을 4(뒤로), 0(그대로), -2(앞으로) 의 형태로 계산하기 쉽게 변경한 후
//			 * 지정된 범위를 넘어가지 않도록 범위를 설정해 준다. 
//			 */
//			float adjustedX = (Gdx.input.getAccelerometerX() - 2f);
//			if (adjustedX < -2f) {
//				adjustedX = -2f;
//			} else if (adjustedX > 2f) {
//				adjustedX = 2f;
//			}//end if else
//			
//			/*
//			 * Y : -2(왼쪽), 0(그대로), 2(오른쪽)
//			 * 이 값은 특별히 변경할 필요는 없으므로,
//			 * 지정된 범위를 넘어가지 않도록 범위만 설정해 준다. 
//			 */
//			float adjustedY = Gdx.input.getAccelerometerY();
//			if (adjustedY < -2f) {
//				adjustedY = -2f;
//			} else if (adjustedY > 2f) {
//				adjustedY = 2f;
//			}//end if else
//			
//			/*
//			 * 2 는 100% 최대 속도 이므로,
//			 * 이를 현실적으로 적용 가능한 형태로 변경한다.
//			 */
//			adjustedX /= 2f;
//			adjustedY /= 2f;
//			
//			/*
//			 * landscape 모드이므로
//			 * 이를 고려하여 위치를 설정해준다.
//			 */
//			x += adjustedY * MAX_HORIZONTAL_SPEED * delta;
//			y += -adjustedX * MAX_VERTICAL_SPEED * delta;
//			
//		} else {
//			
//			/*
//			 * 각 키 입력에 따라서 x/y 좌표를 움직여 준다.
//			 */
//			if (Gdx.input.isKeyPressed(Keys.UP)) {
//				y += MAX_VERTICAL_SPEED * delta;
//			} else if (Gdx.input.isKeyPressed(Keys.DOWN)) {
//				y -= MAX_VERTICAL_SPEED * delta;
//			} else if (Gdx.input.isKeyPressed(Keys.LEFT)) {
//				x -= MAX_HORIZONTAL_SPEED * delta;
//			} else if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
//				x += MAX_HORIZONTAL_SPEED * delta;
//			}//end if else
//			
//		}//end if
//		
//		/*
//		 * 
//		 * ship 이 stage 를 벗어나지 않도록
//		 * 경계조건을 설정해 준다.
//		 * 
//		 */
//		
//		Stage stage = getStage();
//		
//		if (x < 0) {
//			x = 0;
//		} else if (x > stage.getWidth() - getWidth()) {
//			x = stage.getWidth() - getWidth();
//		}//end if else
//		
//		if (y < 0) {
//			y = 0;
//		} else if (y > stage.getHeight() - getHeight()) {
//			y = stage.getHeight() - getHeight();
//		}//end if else
//		
//		
//		setX(x);
//		setY(y);
//		
//	}//end of moveShip()
	
	
	
	/**
	 * 최대 가속도
	 */
	private static final float MAX_ACCELERATION = 8f;
	
	/*
	 * Tutorial 11.
	 * 
	 * Vector 를 사용하여 계산 하도록 변경 !!
	 */
	
	/**
	 * Ship 을 움직입니다.
	 * 
	 * Ship 은 시간 변화량 (delta, δ) 에 따른 움직임 (픽셀/초) 로 계산합니다.
	 * 
	 * @param delta 시간 변화량 (초)
	 */
	public void moveShip(float delta) {
		
		/*
		 * 현재 위치 지정...
		 */
		this.position.set(getX(), getY());
		
		
		/*
		 * 가속도 설정
		 */
		
		/*
		 * 
		 * Tutorial 10.
		 * 
		 * 가속도계가 사용 가능한 지 확인하여,
		 * 사용 가능하다면 이를 입력 장치로 사용하고
		 * 그렇지 않은 경우에는 키보드 입력을 사용하도록 한다.
		 * 
		 */
		if (Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer)) {
			
			if (Tyrian.DEV_MODE) {
				Gdx.app.log(Tyrian.LOG, "Accelerometer X/Y/Z : " + 
						Gdx.input.getAccelerometerX() + " / " + 
						Gdx.input.getAccelerometerY() + " / " + 
						Gdx.input.getAccelerometerZ());
			}//end if
			
			/*
			 * 가속도계를 사용하여 Ship 을 움직이는 경우에는
			 * 가속도계가 던져주는 값을 보정하여 움직임을 지정해야 함
			 * 
			 * 참고로, 현재 화면은 landscape 모드로 동작하므로,
			 * 가속도계가 던져주는 값은 Ship 의 X/Y 좌표와 반대임
			 */
			
			/*
			 * X : 4(뒤로), 2(그대로), 0(앞으로)
			 * 이 값을 4(뒤로), 0(그대로), -2(앞으로) 의 형태로 계산하기 쉽게 변경한 후
			 * 지정된 범위를 넘어가지 않도록 범위를 설정해 준다. 
			 */
			float accelerationX = (Gdx.input.getAccelerometerX() - 2f);
			if (accelerationX < -2f) {
				accelerationX = -2f;
			} else if (accelerationX > 2f) {
				accelerationX = 2f;
			}//end if else
			
			/*
			 * Y : -2(왼쪽), 0(그대로), 2(오른쪽)
			 * 이 값은 특별히 변경할 필요는 없으므로,
			 * 지정된 범위를 넘어가지 않도록 범위만 설정해 준다. 
			 */
			float accelerationY = Gdx.input.getAccelerometerY();
			if (accelerationY < -2f) {
				accelerationY = -2f;
			} else if (accelerationY > 2f) {
				accelerationY = 2f;
			}//end if else
			
			/*
			 * 2 는 100% 최대 가속도를 의미하므로,
			 * 이를 현실적으로 적용 가능한 형태로 변경한다.
			 */
			accelerationX /= 2f;
			accelerationY /= 2f;
			
			/*
			 * landscape 모드이므로
			 * 이를 고려하여 가속도를 설정해준다.
			 */
			this.acceleration.x = accelerationY * MAX_ACCELERATION;
			this.acceleration.y = - accelerationX * MAX_ACCELERATION;
			
		} else {
			
			/*
			 * 각 키 입력에 따라서 가속도를 조절해 준다.
			 */
			this.acceleration.x = (Gdx.input.isKeyPressed(Keys.LEFT) ? - MAX_ACCELERATION : 
				(Gdx.input.isKeyPressed(Keys.RIGHT) ? MAX_ACCELERATION : 0));
			this.acceleration.y = (Gdx.input.isKeyPressed(Keys.UP) ? MAX_ACCELERATION : 
				(Gdx.input.isKeyPressed(Keys.DOWN) ? - MAX_ACCELERATION : 0));
			
		}//end if
		
		/*
		 * 속도를 설정 한다.
		 * 
		 * 속도 = 가속도 * 시간 이므로,...
		 * 
		 * 1) 위의 공식을 사용하여 속도의 변화량 계산
		 * 2) 기존 속도에 변화된 속도 반영
		 */
		this.acceleration.mul(delta);
		this.velocity.add(this.acceleration);
		
		/*
		 * 설정된 속도가 최대 속도를 넘지 않는지 체크 !
		 */
		if (this.velocity.x < 0) {
			this.velocity.x = Math.max(this.velocity.x, - MAX_HORIZONTAL_SPEED * delta);
		} else if (this.velocity.x > 0) {
			this.velocity.x = Math.min(this.velocity.x, MAX_HORIZONTAL_SPEED * delta);
		}//end if else
		
		if (this.velocity.y < 0) {
			this.velocity.y = Math.max(this.velocity.y, - MAX_VERTICAL_SPEED * delta);
		} else if (this.velocity.y > 0) {
			this.velocity.y = Math.min(this.velocity.y, MAX_VERTICAL_SPEED * delta);
		}//end if else
		
		
		/*
		 * 위치를 설정해준다.
		 */
		this.position.add(this.velocity);
		
		
		/*
		 * 
		 * ship 이 stage 를 벗어나지 않도록
		 * 경계조건을 설정해 준다.
		 * 
		 * 경계에 들어가면 더이상 속도의 변화가 없도록
		 * velocity 를 초기화 한다.
		 */
		
		Stage stage = getStage();
		
		if (this.position.x < 0) {
			this.position.x = 0;
			this.velocity.x = 0;
		} else if (this.position.x > stage.getWidth() - getWidth()) {
			this.position.x = stage.getWidth() - getWidth();
			this.velocity.x = 0;
		}//end if else
		
		if (this.position.y < 0) {
			this.position.y = 0;
			this.velocity.y = 0;
		} else if (this.position.y > stage.getHeight() - getHeight()) {
			this.position.y = stage.getHeight() - getHeight();
			this.velocity.y = 0;
		}//end if else
		
		if (Tyrian.DEV_MODE) {
			Gdx.app.log(Tyrian.LOG, "this.acceleration x/y : " + 
					this.acceleration.x + " / " + this.acceleration.y);
			Gdx.app.log(Tyrian.LOG, "this.velocity x/y : " + 
					this.velocity.x + " / " + this.velocity.y);
		}//end if
		
		
		/*
		 * 최종 위치 설정
		 */
		setX(this.position.x);
		setY(this.position.y);
		
	}//end of moveShip()
		

}
//end of Ship2D.java